package com.yc.springboot.favorite.anno;

import com.baomidou.mybatisplus.annotation.TableId;
import com.yc.springboot.favorite.entity.Fav;
import com.yc.springboot.favorite.mapper.FavMapper;
import org.apache.ibatis.annotations.*;

import java.lang.reflect.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.*;

public class SqlSession {

    private String url = "jdbc:mysql://localhost:3306/favorite";
    private String user = "root";
    private String password = "123456";

    public <T> T getMapper(Class<T> cls) {
        InvocationHandler a; // 代理对象
        T mapper = (T) Proxy.newProxyInstance(cls.getClassLoader(),
                new Class[]{cls}, (proxy, method, args) -> {
                    System.out.println("method = " + method);
                    System.out.println("args = " + Arrays.toString(args));
                    Object ret = null;
                    if (method.isAnnotationPresent(Select.class)) {
                        ret = invokeSelect(method, args);
                    } else if (method.isAnnotationPresent(Insert.class)) {
                        ret = invokeInsert(method, args);
                    } else if (method.isAnnotationPresent(Update.class)) {
                        ret = invokeUpdate(method, args);
                    } else if (method.isAnnotationPresent(Delete.class)) {
                        ret = invokeDelete(method, args);
                    } else if (method.getName().equals("updateById")) {
                        ret = updateById(method,args);
                    }
                    return ret;
                }
        );
        return mapper;
    }

    private Object invokeDelete(Method method, Object[] args) {
        return null;
    }

    private Object invokeUpdate(Method method, Object[] args) {
        return null;
    }

    private Object invokeInsert(Method method, Object[] args) {
        return null;
    }

    private Object invokeSelect(Method method, Object[] args) {
        System.out.println("-------------------invokeSelect-------------------");
        Select select = method.getAnnotation(Select.class);
        String sql = select.value()[0];
        // 获取参数和收集参数
        Map<String, Object> paramMap = new HashMap<>();
        if (args == null || args.length == 0) {
            return null;
        } else {
            int index = 0;
            for (Parameter parameter : method.getParameters()) {
                if (parameter.isAnnotationPresent(Param.class)) {
                    String name = parameter.getAnnotation(Param.class).value();
                    Object value = args[index];
                    paramMap.put(name, value);
                } else {
                    // TODO 没有加@Param 注解的参数
                }
                index++;
            }
        }
        List<Object> params = new ArrayList<>();
        while (sql.contains("#")) {
            int indexBegin = sql .indexOf("#");
            int indexEnd = sql.indexOf("}", indexBegin + 1);
            String name = sql.substring(indexBegin + 2, indexEnd);
            Object value = paramMap.get(name);
            params.add(value);
            sql = sql.replaceFirst("#\\{.+?}", "?");
        }
        System.out.println("sql = " + sql);
        System.out.println("params = " + params);

        // TODO 执行语句
        System.out.println("返回值类型 " + method.getReturnType());
        Class c= getGenericTypeByMethodReturn(method);
        System.out.println("泛型类型 = " + c);

        try (Connection conn = DriverManager.getConnection(url, user, password);
             PreparedStatement ps = conn.prepareStatement(sql)) {

            // 设置参数
            for (int i = 0; i < params.size(); i++) {
                ps.setObject(i + 1, params.get(i));
            }

            // 处理结果集
            try (ResultSet rs = ps.executeQuery()) {
                List ret = new ArrayList();
                while (rs.next()) {
                    Object o = c.newInstance();
                    Field[] fields = c.getDeclaredFields(); // 类的属性[field = private java.lang.Integer com.yc.favorite.entity.Fav.id,
                                                            // field = private java.lang.String com.yc.favorite.entity.Fav.name ......
                    for (Field field : fields) {
                        field.setAccessible(true); // 暴力反射
                        String property = field.getName(); // 属性名  id name addr tags intro image uid opens sort ......
                        Object value = rs.getObject(property); // 从结果集中获取属性值
                        field.set(o, value); // 给对象的属性赋值
                    }
                    ret.add(o);

                }
                return ret;
            }
        } catch (Exception e) {
            throw new RuntimeException("SQL执行失败", e);
        }

        // 通过反射封装结果集 假如rs得到了Fav泛型的结果集
    }
//try {
//        // 模拟结果集
//        ResultSet rs =;
//        List ret = new ArrayList();
//        while (rs.next()) {
//            Object o = c.newInstance();
//            Field[] fields = c.getDeclaredFields(); //
//            for (Field field : fields) {
//                System.out.println("field = " + field);
//                field.setAccessible(true);
//                String property = field.getName();
//                System.out.println("property = " + property);
//                Object value = rs.getObject(property);
//            }
//        }
//    } catch (SQLException e) {
//        throw new RuntimeException(e);
//    } catch (InstantiationException e) {
//        throw new RuntimeException(e);
//    } catch (IllegalAccessException e) {
//        throw new RuntimeException(e);
//    }

    private Object updateById(Method method, Object[] args) {
        //构建语句
        //表名
        String tableName = method.getDeclaringClass().getSimpleName();
        //主键字段名
        String idName = null;
        Object idValue = null;
        //统计更新字段
        Map<String,Object> paramMap = new LinkedHashMap<>();
        try {
            for (Field field : args[0].getClass().getDeclaredFields()) {
                field.setAccessible(true);
                //todo 如何判断静态属性
                if (field.getName().equals("serialVersionUID")){
                    continue;
                }
                if (field.isAnnotationPresent(TableId.class)){
                    idName = field.getName();
                    idValue = field.get(args[0]);
                }else {
                    String columnName = field.getName();
                    Object value = field.get(args[0]);
                    if (columnName != null){
                        paramMap.put(columnName,value);
                    }
                }
                paramMap.put(field.getName(),field.get(args[0]));
            }
        }catch (Exception e) {
            throw new RuntimeException(e);
        }

        String sql = "update %s set %s where %s=?";
        String sets = "";
        for (String column : paramMap.keySet()) {
            sets += column + "=?,";
        }
        sets = sets.substring(0,sets.length()-1);
        sql = String.format(sql,tableName,sets,idName);
        List<Object> params = new ArrayList<>();
        params.add(idValue);

        System.out.println("sql = " + sql);
        System.out.println("params = " + params);
        return null;
    }
    public static Class getGenericTypeByMethodReturn(Method method){
        Type returnType = method.getGenericReturnType();
        if (returnType instanceof ParameterizedType){
            ParameterizedType pt=(ParameterizedType)returnType;
            Type[] actualTypeArguments = pt.getActualTypeArguments();
            String cls = actualTypeArguments[0].toString().substring(".class".length());
            try {
                return Class.forName(cls);
            }catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        throw new RuntimeException("返回值类型不是泛型类型");
    }

    public static void main(String[] args) {
        SqlSession sqlSession = new SqlSession();
        FavMapper favMapper = sqlSession.getMapper(FavMapper.class);
        List<Fav> list = favMapper.queryById(2, 1);
        Fav fav = new Fav();
        fav.setId(1);
        fav.setName("淘宝");
        fav.setAddr("taobao.com");
        favMapper.updateById(fav);
    }
}
